package org.jeecg.modules.cosmetic.handler;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.api.dto.CategoryDTO;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.cosmetic.constant.ExplainConstants;
import org.jeecg.modules.cosmetic.constant.enums.*;
import org.jeecg.modules.cosmetic.entity.*;
import org.jeecg.modules.cosmetic.model.dto.AppraisalConclusionDTO;
import org.jeecg.modules.cosmetic.model.dto.ProductPageDTO;
import org.jeecg.modules.cosmetic.model.vo.ProductPageVO;
import org.jeecg.modules.cosmetic.model.vo.ProductStatusVO;
import org.jeecg.modules.cosmetic.model.vo.ProductVO;
import org.jeecg.modules.cosmetic.service.*;
import org.jeecg.modules.cosmetic.utils.CurrentUserUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static org.jeecg.modules.cosmetic.constant.RedisConstants.PRODUCT_SYS_CODE;

/**
 * @author scx
 * @date 2022/7/7 3:00 下午
 */
@Service
@Slf4j
public class ProductHandler {

    @Resource
    private ITProductService productService;
    @Resource
    private ISysBaseAPI sysBaseApiImpl;
    @Resource
    private ITSecurityEvaluationService securityEvaluationService;
    @Resource
    private ITRawmaterialService rawmaterialService;
    @Resource
    private ITFormulaService formulaService;
    @Resource
    private ITProductMaterialService productMaterialService;
    @Resource
    private ITProductCategoryService productCategoryService;
    @Resource
    private RedisUtil redisUtil;


    /**
     * @param productVO
     * @return org.jeecg.common.api.vo.Result<java.lang.Void>
     * @description 更新产品信息
     */
    public Result<?> modifyProduct(ProductVO productVO) {
        TProduct product = new TProduct();
        BeanUtils.copyProperties(productVO, product);
        boolean update = productService.updateById(product);
        if (!update) {
            return Result.error("修改产品信息失败");
        }
        try {
            conditionCheck(product.getId(), product.getProductType());
            return Result.OK();
        } catch (Exception e) {
            log.error("产品更新-获取评估结论异常：{}", e.getMessage());
            return Result.error("修改产品信息异常");
        }

    }

    /**
     * @param productPageVO
     * @return org.jeecg.common.api.vo.Result<com.baomidou.mybatisplus.core.metadata.IPage < org.jeecg.modules.cosmetic.model.dto.ProductPageDTO>>
     * @description 分页查询产品列表
     */
    public Result<IPage<ProductPageDTO>> getProductPage(ProductPageVO productPageVO) {
        LambdaQueryWrapper<TProduct> queryWrapper = new LambdaQueryWrapper<TProduct>()
                .like(StringUtils.isNotEmpty(productPageVO.getProductName()), TProduct::getChineseName, productPageVO.getProductName())
                .orderByDesc(TProduct::getCreateTime);
        if (productPageVO.getStatus() == null) {
            queryWrapper.ge(TProduct::getStatus, ProductStatusEnum.UNFINISHED.getCode());
        } else {
            queryWrapper.eq(TProduct::getStatus, productPageVO.getStatus());
        }
        Page<TProduct> page = new Page<>(productPageVO.getPageNo(), productPageVO.getPageSize());
        IPage<TProduct> pageList = productService.page(page, queryWrapper);
        if (pageList == null) {
            return Result.error("产品分页异常");
        }
        List<ProductPageDTO> productPageDTOS = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(pageList.getRecords())) {
            pageList.getRecords().stream().map(m -> {
                ProductPageDTO productPageDTO = new ProductPageDTO();
                productPageDTO.setId(m.getId());
                productPageDTO.setCName(m.getChineseName());
                productPageDTO.setEName(m.getEnglishName());
                productPageDTO.setFilingName(m.getFilingName());
                productPageDTO.setOperateName(m.getCreateBy());
                productPageDTO.setStatus(m.getStatus());
                productPageDTO.setOperateTime(DateUtils.date2Str(m.getCreateTime(), DateUtils.datetimeFormat.get()));
                productPageDTO.setRemark(m.getRemark());
                productPageDTO.setReportUrl(m.getReportUrl());
                return productPageDTO;
            }).collect(Collectors.toCollection(() -> productPageDTOS));
        }
        IPage<ProductPageDTO> pageDTOIPage = new Page<>();
        pageDTOIPage.setCurrent(page.getCurrent());
        pageDTOIPage.setRecords(productPageDTOS);
        pageDTOIPage.setPages(page.getPages());
        pageDTOIPage.setTotal(page.getTotal());
        pageDTOIPage.setSize(page.getSize());
        return Result.ok(pageDTOIPage);
    }

    /**
     * 根据主键查询产品详情
     *
     * @param id
     * @return
     */
    public Result<TProduct> getProductByPrimary(Long id) {
        TProduct tProduct = productService.getById(id);
        Optional.ofNullable(tProduct).orElseThrow(() -> new JeecgBootException("产品信息不存在"));
        return Result.OK(tProduct);
    }

    /**
     * @param vo
     * @return org.jeecg.common.api.vo.Result<?>
     * @description 作废
     */
    public Result<?> productInvalid(ProductStatusVO vo) {
        TProduct product = productService.getById(vo.getId());
        Optional.ofNullable(product).orElseThrow(() -> new JeecgBootException("产品信息不存在"));
        if (product.getStatus() == ProductStatusEnum.INVALID.getCode()) {
            throw new JeecgBootException("当前产品已作废，请勿重复操作");
        }
        product.setStatus(ProductStatusEnum.INVALID.getCode());
        product.setRemark(vo.getReason());
        productService.updateById(product);
        //删除系统编码缓存
        redisUtil.hdel(PRODUCT_SYS_CODE, product.getOriginSysCode());
        return Result.OK();
    }

    /**
     * @param id
     * @return org.jeecg.common.api.vo.Result<?>
     * @description 修订
     */
    public Result<?> productRevise(Long id) {
        //1、先作废
        TProduct product = productService.getById(id);
        Optional.ofNullable(product).orElseThrow(() -> new JeecgBootException("产品信息不存在"));
        product.setStatus(ProductStatusEnum.INVALID.getCode());
        product.setRemark("安评报告修订，系统自动作废");
        productService.updateById(product);

        //2、再插入
        TProduct newProduct = new TProduct();
        BeanUtils.copyProperties(product, newProduct);
        newProduct.setId(null);
        newProduct.setStatus(ProductStatusEnum.UNFINISHED.getCode());
        newProduct.setRemark("");
        newProduct.setReportUrl("");
        newProduct.setCreateTime(new Date());
        newProduct.setUpdateTime(new Date());
        newProduct.setCreateBy(CurrentUserUtil.authInfo().getName());
        newProduct.setUpdateBy(CurrentUserUtil.authInfo().getName());
        //3、重置系统编码
        queryNewSysCodeFromData(newProduct);
        productService.save(newProduct);
        //4、初始化数据
        copyProductFormula(id, newProduct.getId());
        return Result.ok(newProduct.getId());
    }

    /**
     * 拷贝产品配方数据
     *
     * @param oldProductId
     * @param newProductId
     */
    private void copyProductFormula(Long oldProductId, Long newProductId) {

        List<TFormula> formulas = formulaService.list(new LambdaQueryWrapper<TFormula>()
                .eq(TFormula::getProductId, oldProductId));
        if (!CollectionUtils.isEmpty(formulas)) {
            formulas.forEach(f -> {
                f.setProductId(newProductId);
            });
            formulaService.updateBatchById(formulas);
        }
        List<TSecurityEvaluation> securityEvaluations = securityEvaluationService.list(new LambdaQueryWrapper<TSecurityEvaluation>()
                .eq(TSecurityEvaluation::getProductId, oldProductId));
        if (!CollectionUtils.isEmpty(securityEvaluations)) {
            securityEvaluations.forEach(f -> {
                f.setProductId(newProductId);
            });
            securityEvaluationService.updateBatchById(securityEvaluations);
        }
        List<TProductMaterial> productMaterials = productMaterialService.list(new LambdaQueryWrapper<TProductMaterial>()
                .eq(TProductMaterial::getProductId, oldProductId));
        if (!CollectionUtils.isEmpty(productMaterials)) {
            productMaterials.forEach(f -> {
                f.setProductId(newProductId);
            });
            productMaterialService.updateBatchById(productMaterials);
        }
    }

    /**
     * 重置系统编码
     *
     * @param newProduct
     */
    private void queryNewSysCodeFromData(TProduct newProduct) {
        String reviseNo = String.valueOf(redisUtil.hget(PRODUCT_SYS_CODE, newProduct.getOriginSysCode()));
        if (StringUtils.isNotEmpty(reviseNo)) {
            long newReviseNo = redisUtil.hincr(PRODUCT_SYS_CODE, newProduct.getOriginSysCode(), 1L);
            newProduct.setSysCode(newProduct.getOriginSysCode() + "R" + newReviseNo);
            return;
        }
        int newReviseNo = 1;
        if (!newProduct.getSysCode().equalsIgnoreCase(newProduct.getOriginSysCode())) {
            reviseNo = newProduct.getSysCode().substring(newProduct.getSysCode().length() - 1);
            newReviseNo = Integer.parseInt(reviseNo);
            newReviseNo++;
        }
        newProduct.setSysCode(newProduct.getOriginSysCode() + "R" + newReviseNo);
        redisUtil.hset(PRODUCT_SYS_CODE, newProduct.getOriginSysCode(), newReviseNo);
    }

    /**
     * 获取产品种类
     *
     * @return
     */
    public Result<List<CategoryDTO>> getProductCategoryList() {
        List<CategoryDTO> categoryDTOS = sysBaseApiImpl.getCategoryByName();
        if (CollectionUtils.isEmpty(categoryDTOS)) {
            return Result.OK(categoryDTOS);
        }
        categoryDTOS.forEach(item -> {
            if (CollectionUtils.isEmpty(item.getChildNodes())) {
                return;
            }
            item.getChildNodes().forEach(child -> {
                TProductCategory productCategory = productCategoryService.getOne(new LambdaQueryWrapper<TProductCategory>()
                        .eq(TProductCategory::getCode, child.getCode()));
                if (ObjectUtil.isNotEmpty(productCategory)) {
                    child.setDailyDosage(productCategory.getDailyDosage());
                    child.setResidentDivisor(productCategory.getResidentDivisor());
                    child.setSource(productCategory.getSource());
                }
            });
        });
        return Result.OK(categoryDTOS);
    }


    /**
     * 评估结论更新
     * 满足条件B则不进行下面的判断
     *
     * @param productId
     * @param productType
     */
    private void conditionCheck(Long productId, Integer productType) {
        List<TSecurityEvaluation> securityEvaluations = securityEvaluationService.list(new LambdaQueryWrapper<TSecurityEvaluation>()
                .eq(TSecurityEvaluation::getProductId, productId));
        if (CollectionUtils.isEmpty(securityEvaluations)) {
            log.error("产品配方成分不存在，productId:{}", productId);
            return;
        }
        //过滤评估结论为空的进行判断
        securityEvaluations.stream().filter(f -> StringUtils.isEmpty(f.getEvaluatedConclusion())).forEach(item -> {
            TRawmaterial rawmaterial = rawmaterialService.getOne(new LambdaQueryWrapper<TRawmaterial>()
                    .eq(TRawmaterial::getIecicNum, item.getIecicNum()));
            if (ObjectUtil.isEmpty(rawmaterial)) {
                return;
            }

            //条件B:根据原料使用目的
            String usedPurpose = getConclusionByUsedPurpose(rawmaterial.getCstsInfo(), productId, rawmaterial.getIecicNum());
            if (StringUtils.isNotEmpty(usedPurpose)) {
                item.setTechnicalSpecification(usedPurpose);
                item.setEvaluatedConclusion(ExplainConstants.B_APPRAISAL_CONCLUSION);
                securityEvaluationService.updateById(item);
                return;
            }
            //条件C:根据产品类型
            AppraisalConclusionDTO appraisalConclusionDTO = getConclusionByProductType(productType, item.getContent(), rawmaterial);
            if (appraisalConclusionDTO != null) {
                item.setAuthorityConclusion(appraisalConclusionDTO.getAuthorityConclusion());
                item.setHighestHistoryUsage(appraisalConclusionDTO.getHighestHistoryUsage());
                item.setEvaluatedConclusion(appraisalConclusionDTO.getEvaluatedConclusion());
                securityEvaluationService.updateById(item);
                return;
            }
            //条件D:根据其他条件
            if (StringUtils.isEmpty(item.getTechnicalSpecification()) && StringUtils.isEmpty(item.getAuthorityConclusion())) {
                AppraisalConclusionDTO appraisalConclusionDTO1 = getByOtherCondition(productType, item.getContent(), rawmaterial);
                if (ObjectUtil.isNotEmpty(appraisalConclusionDTO1)) {
                    item.setHighestHistoryUsage(appraisalConclusionDTO1.getHighestHistoryUsage());
                    item.setEvaluatedConclusion(appraisalConclusionDTO1.getEvaluatedConclusion());
                    securityEvaluationService.updateById(item);
                }
            }
        });
    }

    /**
     * 根据使用目获取评估结果
     * requirement为空，不处理
     * requirement不为空，查询使用目的，如果使用目的存在多且都为字典中的数据，则取数值最小（优先级最高）
     * 的使用目的，否则直接返回requirement
     *
     * @param requirement
     * @return
     */
    private String getConclusionByUsedPurpose(String requirement, Long productId, String iecicNum) {
        if (StringUtils.isEmpty(requirement)) {
            return null;
        }
        List<TFormula> formulas = formulaService.list(new LambdaQueryWrapper<TFormula>()
                .eq(TFormula::getProductId, productId)
                .eq(TFormula::getIecicNum, iecicNum));
        if (CollectionUtils.isEmpty(formulas)) {
            return requirement;
        }
        Set<String> majorUsedSet = formulas.stream().map(m -> {
            if (m.getParentId() == null) {
                return m.getMajorUsePurpose();
            }
            TFormula parent = formulaService.getById(m.getParentId());
            return parent.getMajorUsePurpose();
        }).collect(Collectors.toSet());

        List<DictModel> dictModels = sysBaseApiImpl.getDictItems(ExplainConstants.USED_PURPOSE_DICT_CODE);
        //过滤字典中使用目的，如果为空，直接返回化妆品安全技术规范要求
        List<DictModel> includeDictList = dictModels.stream().filter(f -> majorUsedSet.contains(f.getText())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(includeDictList)) {
            return requirement;
        }
        //如果不为空，返回优先级最高的（即数值最小的）
        DictModel dictModel = includeDictList.stream().min(Comparator.comparing(DictModel::getValue)).orElse(null);
        if (dictModel == null) {
            return requirement;
        }
        //查询requirement是否包含该原料不作为{使用目的}，包含则截取之后的文字，否则直接返回所有
        String checkConstants = String.format(ExplainConstants.USED_PURPOSE_CHECK, dictModel.getText());
        if (requirement.contains(checkConstants)) {
            return requirement.substring(requirement.indexOf(checkConstants) + 1);
        }
        return requirement;
    }

    /**
     * 根据产品类型获取评估结果
     *
     * @param productType
     * @param elementPurity
     * @param rawmaterial
     * @return
     */
    private AppraisalConclusionDTO getConclusionByProductType(Integer productType, String elementPurity, TRawmaterial rawmaterial) {

        switch (ProductTypeEnum.getProductTypeEnumByType(productType)) {
            case RINSING:
                //判断CIR淋洗含量是否大于等于原料实际成分含量
                if (StringUtils.isEmpty(rawmaterial.getCirRinseTop())) {
                    log.error("原料中成分：{} CIR淋洗含量为空", rawmaterial.getCnName());
                    return null;
                }
                if (!checkContentValue(rawmaterial.getCnName(), rawmaterial.getCirRinseTop())) {
                    return null;
                }
                if (StringUtils.isEmpty(rawmaterial.getAoacCir())) {
                    log.error("原料中成分：{} 权威机构评估结论为空", rawmaterial.getCnName());
                    return null;
                }

                RinsingConclusionEnum rinsingConclusionEnum = RinsingConclusionEnum.getByType(rawmaterial.getAoacCir());
                if (new BigDecimal(rawmaterial.getCirRinseTop()).compareTo(new BigDecimal(elementPurity)) >= 0 && rinsingConclusionEnum != null) {
                    return rinsingConclusionEnum.getConclusion(rawmaterial.getCirRinseTop(),
                            rawmaterial.getResideTopNum(),
                            rawmaterial.getRinseTopNum(),
                            elementPurity);
                }
                if (rawmaterial.getAoacCir().equals("安全")) {
                    return AppraisalConclusionDTO.builder()
                            .authorityConclusion(MessageFormat.format(ExplainConstants.C_RINSING_AUTHORITY_CONCLUSION_PARAM, rawmaterial.getCirRinseTop()))
                            .evaluatedConclusion(ExplainConstants.C_RINSING_APPRAISAL_CONCLUSION)
                            .build();
                }
            case RESIDE:
            case USED_CLEAN:
                //判断CIR驻留含量是否大于等于原料实际成分含量
                if (StringUtils.isEmpty(rawmaterial.getCirResideTop())) {
                    log.error("原料中CIR驻留含量为空");
                    return null;
                }
                if (!checkContentValue(rawmaterial.getCnName(), rawmaterial.getCirResideTop())) {
                    return null;
                }
                if (new BigDecimal(rawmaterial.getCirResideTop()).compareTo(new BigDecimal(elementPurity)) < 0) {
                    return null;
                }
                if (StringUtils.isEmpty(rawmaterial.getAoacCir())) {
                    return null;
                }
                ResideConclusionEnum resideConclusionEnum = ResideConclusionEnum.getByType(rawmaterial.getAoacCir());
                if (resideConclusionEnum == null) {
                    log.error("权威机构评估结论未命中，无法评估");
                    return null;
                }
                return resideConclusionEnum.getConclusion(rawmaterial.getCirResideTop(),
                        rawmaterial.getResideTopNum(),
                        elementPurity);

            default:
                log.error("产品类型不明确：{}", productType);
                break;
        }
        return null;
    }

    /**
     * 根据其他条件返回评估结果
     *
     * @param productType
     * @param elementPurity
     * @param rawmaterial
     * @return
     */
    private AppraisalConclusionDTO getByOtherCondition(Integer productType, String elementPurity, TRawmaterial rawmaterial) {
        AppraisalConclusionDTO appraisalConclusionDTO = new AppraisalConclusionDTO();

        switch (ProductTypeEnum.getProductTypeEnumByType(productType)) {
            case RINSING:
                if (StringUtils.isEmpty(rawmaterial.getRinseTopNum()) || !checkContentValue(rawmaterial.getCnName(), rawmaterial.getRinseTopNum())) {
                    log.error("当前产品类型：{},评估结果为无法评估，成分名称：{},IECIC含量：{}", ProductTypeEnum.RINSING.getMsg(), rawmaterial.getCnName(), rawmaterial.getRinseTopNum());
                    appraisalConclusionDTO.setEvaluatedConclusion(ExplainConstants.C_CANNOT_CONCLUSION);
                    return appraisalConclusionDTO;
                }
                if (new BigDecimal(rawmaterial.getRinseTopNum()).compareTo(new BigDecimal(elementPurity)) >= 0) {
                    appraisalConclusionDTO.setHighestHistoryUsage(rawmaterial.getRinseTopNum());
                    appraisalConclusionDTO.setEvaluatedConclusion(ExplainConstants.C_RINSING_APPRAISAL_CONCLUSION_PARAM1 + "，可安全使用。");
                    return appraisalConclusionDTO;
                }

                if (StringUtils.isNotEmpty(rawmaterial.getResideTopNum()) &&
                        checkContentValue(rawmaterial.getCnName(), rawmaterial.getResideTopNum()) &&
                        new BigDecimal(rawmaterial.getResideTopNum()).compareTo(new BigDecimal(elementPurity)) >= 0) {
                    appraisalConclusionDTO.setHighestHistoryUsage("驻留：" + rawmaterial.getResideTopNum());
                    appraisalConclusionDTO.setEvaluatedConclusion(ExplainConstants.C_RINSING_APPRAISAL_CONCLUSION_PARAM2 + "，可安全使用。");
                    return appraisalConclusionDTO;
                }
                appraisalConclusionDTO.setEvaluatedConclusion(ExplainConstants.C_CANNOT_CONCLUSION);
                return appraisalConclusionDTO;
            case RESIDE:
            case USED_CLEAN:
                if (StringUtils.isNotEmpty(rawmaterial.getResideTopNum()) &&
                        checkContentValue(rawmaterial.getCnName(), rawmaterial.getResideTopNum()) &&
                        new BigDecimal(rawmaterial.getResideTopNum()).compareTo(new BigDecimal(elementPurity)) >= 0) {
                    appraisalConclusionDTO.setHighestHistoryUsage(rawmaterial.getResideTopNum());
                    appraisalConclusionDTO.setEvaluatedConclusion(ExplainConstants.C_RESIDE_APPRAISAL_CONCLUSION_PARAM + "，可安全使用。");
                    return appraisalConclusionDTO;
                }
                appraisalConclusionDTO.setEvaluatedConclusion(ExplainConstants.C_CANNOT_CONCLUSION);
                return appraisalConclusionDTO;
            default:
                log.error("产品类型不明确：{}", productType);
                break;
        }
        return appraisalConclusionDTO;
    }

    /**
     * 校验原料CIR含量
     *
     * @param cnName
     * @param content
     * @return
     */
    private boolean checkContentValue(String cnName, String content) {
        //校验是否整数
        boolean intFlag = Pattern.compile("^-?[1-9]\\d*$").matcher(content).find();
        if (intFlag) {
            return true;
        }
        //校验是否小数
        boolean doubleFlag = Pattern.compile("^-?([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0)$").matcher(content).find();
        if (doubleFlag) {
            return true;
        }
        log.error("原料CIR含量非数值，无法校验，{},{}", cnName, content);
        return false;
    }

}
